const fs = require('fs');
const path = require('path');
const multer = require('multer');
const { ensureUploadDir } = require('../middlewares/fileMiddleware');
const { getLogger } = require('../logger');
const logger = getLogger('services.uploadService');
const config = require('../../config');

// 上传服务类
class UploadService {
  /**
   * 检查文件是否存在
   * @param {string} filePath - 文件路径
   * @returns {Promise<boolean>}
   */
  static async checkFileExists(filePath) {
    try {
      await fs.promises.access(filePath, fs.constants.F_OK);
      return true;
    } catch (error) {
      return false;
    }
  }

  /**
   * 获取头像上传配置
   * @param {number} userId - 用户ID
   * @returns {Object} - multer配置
   */
  static getAvatarUploadConfig(userId) {
    const storage = multer.diskStorage({
      destination: (req, file, cb) => {
        const uploadPath = config.upload.dirs.avatars;
        ensureUploadDir(uploadPath);
        cb(null, uploadPath);
      },
      filename: (req, file, cb) => {
        // 获取文件扩展名
        const ext = path.extname(file.originalname).toLowerCase() || '.jpg';
        
        try {
          // 从req.fileHash获取hash值（从header中获取的）
          const fileHash = req.fileHash;
          
          // 记录hash值获取状态
          logger.info('头像上传hash值状态', {
            userId,
            headerHash: fileHash,
            originalFilename: file.originalname
          });
          
          // 明确区分hash值使用情况
          let filename;
          if (fileHash) {
            // 当存在hash值时，使用hash值作为文件名
            filename = `${fileHash}${ext}`;
            logger.info('使用hash值作为文件名', { userId, filename, fileHash, originalFilename: file.originalname });
            
            // 检查文件是否已存在
            const filePath = path.join(config.upload.dirs.avatars, filename);
            if (fs.existsSync(filePath)) {
              logger.info('文件已存在', { userId, filename });
              req.fileAlreadyExists = true;
              req.existingFilePath = `${config.upload.urls.avatars}/${filename}`;
            }
          } else {
            // 没有hash值时使用时间戳和用户ID命名
            const timestamp = Date.now();
            filename = `${userId}_${timestamp}${ext}`;
            logger.warn('未找到hash值，使用时间戳作为文件名', { userId, filename, originalFilename: file.originalname });
          }
          
          logger.info('头像文件名确定', { userId, filename });
          cb(null, filename);
        } catch (error) {
          logger.error('获取头像文件名时出错', { userId, error: error });
          // 发生错误时使用默认文件名
          const timestamp = Date.now();
          cb(null, `${userId}_${timestamp}${ext}`);
        }
      }
    });
    
    return {
      storage,
      fileFilter: (req, file, cb) => {
        const allowed = (config.upload?.allowed?.avatar) || [];
        if (allowed.length && !allowed.includes(file.mimetype)) {
          return cb(new Error('文件类型不允许'), false);
        }
        cb(null, true);
      },
      limits: {
        fileSize: config.upload.limits.avatarMaxSize,
      }
      // 文件类型验证将由路由中的validateFileContent中间件处理
    };
  }
  
  /**
   * 获取文件上传配置
   * @param {number} userId - 用户ID
   * @returns {Object} - multer配置
   */
  static getFileUploadConfig(userId) {
    const storage = multer.diskStorage({
      destination: (req, file, cb) => {
        const uploadPath = config.upload.dirs.files;
        ensureUploadDir(uploadPath);
        cb(null, uploadPath);
      },
      filename: (req, file, cb) => {
        // 获取文件扩展名
        const ext = path.extname(file.originalname).toLowerCase();
        // 生成时间戳
        const timestamp = Date.now();
        
        let fileHash = null;
        
        try {
          // 详细记录请求状态
          logger.info('开始获取文件上传配置', {
            userId,
            requestUrl: req.originalUrl,
            requestMethod: req.method,
            bodyKeys: req.body ? Object.keys(req.body) : [],
            bodyContent: req.body ? JSON.stringify(req.body) : '无请求体',
            headers: JSON.stringify(req.headers),
            query: JSON.stringify(req.query),
            hasFile: !!req.file,
            fileInfo: req.file ? JSON.stringify(req.file) : '无文件对象'
          });
          
          // 1. 首先尝试从file对象中获取hash属性
          if (file && file.hash) {
            fileHash = file.hash;
            logger.info('从file.hash属性获取到hash值', { userId, fileHash });
          }
          
          // 2. 尝试从req.body中获取hash（formData中的单独字段）
          if (!fileHash && req.body && typeof req.body === 'object') {
            if (req.body.hash) {
              fileHash = req.body.hash;
              logger.info('从req.body.hash获取到hash值', { userId, fileHash });
            } else if (req.body.fileHash) {
              fileHash = req.body.fileHash;
              logger.info('从req.body.fileHash获取到hash值', { userId, fileHash });
            } else if (req.body.filehash) {
              fileHash = req.body.filehash;
              logger.info('从req.body.filehash获取到hash值', { userId, fileHash });
            } else if (req.body.file && req.body.file.hash) {
              fileHash = req.body.file.hash;
              logger.info('从req.body.file.hash获取到hash值', { userId, fileHash });
            }
          }
          
          // 3. 检查查询参数
          if (!fileHash && req.query) {
            if (req.query.hash) {
              fileHash = req.query.hash;
              logger.info('从查询参数hash获取到hash值', { userId, fileHash });
            } else if (req.query.fileHash) {
              fileHash = req.query.fileHash;
              logger.info('从查询参数fileHash获取到hash值', { userId, fileHash });
            }
          }
          
          // 4. 检查请求头
          if (!fileHash && req.headers) {
            if (req.headers['x-file-hash']) {
              fileHash = req.headers['x-file-hash'];
              logger.info('从请求头x-file-hash获取到hash值', { userId, fileHash });
            } else if (req.headers['x-filehash']) {
              fileHash = req.headers['x-filehash'];
              logger.info('从请求头x-filehash获取到hash值', { userId, fileHash });
            } else if (req.headers['hash']) {
              fileHash = req.headers['hash'];
              logger.info('从请求头hash获取到hash值', { userId, fileHash });
            }
          }
          
          // 5. 检查file对象的其他可能的hash属性
          if (!fileHash && file && file.fileHash) {
            fileHash = file.fileHash;
            logger.info('从file.fileHash属性获取到hash值', { userId, fileHash });
          }
          
          logger.info('hash值获取完成', { userId, fileHash: fileHash || '未获取到' });
        } catch (error) {
          logger.error('获取hash值时出错', { userId, error: error, stack: error.stack });
        }
        
        // 确保fileHash是字符串类型且不为空
        fileHash = String(fileHash || '').trim();
        
        // 明确区分hash值使用情况
        let filename;
        if (fileHash) {
          // 当存在hash值时，完全使用hash值作为文件名
          filename = `${fileHash}${ext}`;
          logger.info('使用hash值作为文件名', { userId, filename, fileHash });
          
          // 检查文件是否已存在
          const filePath = path.join(config.upload.dirs.files, filename);
          if (fs.existsSync(filePath)) {
            logger.info('文件已存在', { userId, filename });
            req.fileAlreadyExists = true;
            req.existingFilePath = `${config.upload.urls.files}/${filename}`;
          }
        } else {
          // 没有hash值时才使用时间戳命名
          filename = `${userId}_${timestamp}_${path.basename(file.originalname, ext)}${ext}`;
          logger.info('使用时间戳作为文件名（未提供hash）', { userId, filename });
        }
        
        cb(null, filename);
      }
    });
    
    return {
      storage,
      fileFilter: (req, file, cb) => {
        const allowed = (config.upload?.allowed?.file) || [];
        if (allowed.length && !allowed.includes(file.mimetype)) {
          return cb(new Error('文件类型不允许'), false);
        }
        cb(null, true);
      },
      limits: {
        fileSize: config.upload.limits.fileMaxSize,
      }
    };
  }
  
  /**
   * 切片上传配置
   * @param {string} fileHash - 文件哈希值
   * @returns {Object} - multer配置
   */
  static getChunkUploadConfig(fileHash) {
    const storage = multer.diskStorage({
      destination: (req, file, cb) => {
        const chunksDir = path.join(config.upload.dirs.chunks, fileHash);
        ensureUploadDir(chunksDir);
        cb(null, chunksDir);
      },
      filename: (req, file, cb) => {
        // 使用切片索引作为文件名
        cb(null, req.body.chunkIndex);
      }
    });
    
    return {
      storage,
      limits: {
        fileSize: config.upload.limits.chunkMaxSize,
      }
    };
  }
  
  /**
   * 创建头像上传中间件
   * @param {number} userId - 用户ID
   * @returns {Function} - multer中间件
   */
  static createAvatarUploadMiddleware(userId) {
    const config = this.getAvatarUploadConfig(userId);
    // 创建multer中间件并启用文件大小限制
    const upload = multer(config);
    // 返回一个函数，直接使用multer处理头像上传请求
    return (req, res, next) => {
      // 执行上传流程
      upload.single('avatar')(req, res, (err) => {
        if (err) {
          logger.error('头像上传中间件错误', { userId, error: err.message });
          return next(err);
        }
        
        // 中间件执行成功后，检查文件是否已存在（如果在filename回调中已设置标志）
        if (req.fileAlreadyExists) {
          logger.info('头像文件已存在标记已设置', { userId, existingFilePath: req.existingFilePath });
        }
        
        // 继续处理请求
        next();
      });
    };
  }
  
  /**
   * 创建文件上传中间件
   * @param {number} userId - 用户ID
   * @returns {Function} - multer中间件
   */
  static createFileUploadMiddleware(userId) {
    const config = this.getFileUploadConfig(userId);
    return multer(config).single('file');
  }
  
  /**
   * 创建切片上传中间件
   * @param {string} fileHash - 文件哈希值
   * @returns {Function} - multer中间件
   */
  static createChunkUploadMiddleware(fileHash) {
    const config = this.getChunkUploadConfig(fileHash);
    return multer(config).single('chunk');
  }
  
  /**
   * 合并文件切片
   * @param {string} fileHash - 文件哈希值
   * @param {string} filename - 文件名
   * @param {number} totalChunks - 总切片数
   * @param {number} userId - 用户ID
   * @returns {Promise<string>} - 返回合并后的文件URL
   */
  static async mergeChunks(fileHash, filename, totalChunks, userId) {
    try {
      const chunksDir = path.join(config.upload.dirs.chunks, fileHash);
      const uploadPath = config.upload.dirs.files;
      ensureUploadDir(uploadPath);
      
      const finalPath = path.join(uploadPath, `${userId}_${fileHash}_${filename}`);
      const writeStream = fs.createWriteStream(finalPath);
      
      // 按顺序写入每个切片
      for (let i = 0; i < totalChunks; i++) {
        const chunkPath = path.join(chunksDir, i.toString());
        if (!fs.existsSync(chunkPath)) {
          throw new Error(`缺少切片: ${i}`);
        }
        
        const chunkData = fs.readFileSync(chunkPath);
        writeStream.write(chunkData);
      }
      
      // 关闭写入流
      writeStream.end();
      
      // 删除临时切片目录
      fs.rmSync(chunksDir, { recursive: true, force: true });
      
      return `${config.upload.urls.files}/${userId}_${fileHash}_${filename}`;
    } catch (error) {
      logger.error('合并文件切片失败', { fileHash, filename, error: error });
      throw error;
    }
  }

  /**
   * 验证上传参数
   * @param {Object} params - 上传参数
   * @returns {Object} - 验证结果 {isValid: boolean, message?: string}
   */
  static validateUploadParams(params) {
    const { fileHash, chunkIndex, totalChunks, filename } = params;
    
    if (!fileHash) {
      return { isValid: false, message: '缺少文件哈希值' };
    }
    
    if (chunkIndex === undefined || chunkIndex === null) {
      return { isValid: false, message: '缺少切片索引' };
    }
    
    if (!totalChunks) {
      return { isValid: false, message: '缺少总切片数' };
    }
    
    if (!filename) {
      return { isValid: false, message: '缺少文件名' };
    }
    
    return { isValid: true };
  }

  /**
   * 生成文件存储路径
   * @param {string} uploadType - 上传类型 (avatars, files, chunks)
   * @param {number} userId - 用户ID
   * @param {string} fileHash - 文件哈希值（可选）
   * @returns {string} - 完整的存储路径
   */
  static getUploadPath(uploadType, userId, fileHash = null) {
    let relativePath = `../data/uploads/${uploadType}`;
    
    if (uploadType === 'chunks' && fileHash) {
      relativePath = `${relativePath}/${fileHash}`;
    }
    
    const fullPath = path.join(__dirname, relativePath);
    ensureUploadDir(fullPath);
    
    return fullPath;
  }

  /**
   * 清理过期的临时文件
   * @param {string} tempDir - 临时目录路径
   * @param {number} maxAge - 最大保留时间（毫秒）
   */
  static cleanupTempFiles(tempDir, maxAge = 24 * 60 * 60 * 1000) {
    const now = Date.now();
    
    try {
      if (!fs.existsSync(tempDir)) {
        return;
      }
      
      const files = fs.readdirSync(tempDir);
      
      files.forEach(file => {
        const filePath = path.join(tempDir, file);
        const stats = fs.statSync(filePath);
        
        if (stats.isDirectory()) {
          // 递归清理子目录
          this.cleanupTempFiles(filePath, maxAge);
          
          // 如果目录为空，删除它
          const subFiles = fs.readdirSync(filePath);
          if (subFiles.length === 0) {
            fs.rmdirSync(filePath);
          }
        } else if (now - stats.mtimeMs > maxAge) {
          // 删除过期文件
          fs.unlinkSync(filePath);
          logger.info('清理过期临时文件', { filePath });
        }
      });
    } catch (error) {
      logger.error('清理临时文件失败', { tempDir, error: error });
    }
  }
}

module.exports = UploadService;